#ifndef __tenacitas_crosswords_concept__layout__h__
#define __tenacitas_crosswords_concept__layout__h__


/** @file */


/** @todo */

// ==> C++ Headers
#include <vector>

// ==> 3rds Headers

// ==> Our Headers
#include <tenacitas.return_code.core/rc.h>
#include "word.h"
#include "coordinate.h"
#include "coordinates.h"

// ==> Namespaces
using namespace std;

// ==> Macro Constants

// ==> Macro Commands


/** ==> namespace of the organization */
namespace tenacitas {
    /** ==> namespace for the project */
    namespace crosswords {
        /** ==> bussiness, concept, displaying, interacting, transmitting,
            storing */ 
        namespace concept {

            // ==> Typedefs 

            // ==> Pre-Declarations
            template <typename supplier>
            class layouts;

            // ==> Attributes 

            // ==> Functions

            // ==> Classes

            /**

               @tparam supplier must define @p text and @p letter

               @tparam supplier::text is the type of string to be
               used: @p std::string, or @p std::wstring, or @p std::u16string, or
               @p std::u32string. 

               @nosubgrouping
            */
            template <typename supplier>
            class layout {

            public:

                // ==> layout - Public Internal Types

                /** */
                typedef typename supplier::text text;

                /** */
                typedef typename text::value_type letter;

                /** */
                typedef typename concept::word<supplier> word;

                /** */
                typedef typename word::category category;

                /** */
                typedef typename concept::coordinates<supplier> coordinates;

                /** */
                typedef concept::coordinate<supplier> coordinate;

                /** */
                typedef typename coordinate::position position;

                /** */
                typedef typename coordinates::index index;

                /** */
                enum orientation : constant_id { undef = 0, hor = 1, ver = 2 };

                /** */
                enum constants : constant_id {
                    coordinate_frontier_colision = 100,
                        coordinate_value_unmatch = 101,
                        could_not_position = 102};
                

                /** */
                typedef uint16_t layout_id;

                // ==> layout - Friends

                /** */
                friend class supplier::organizer;

                /** Class allowed to access private members of @p layout, in
                    order to perform tests */
                friend class layout_tester;

                /** */
                friend std::ostream & operator << (std::ostream & p_out,
                                                   const layout & p_layout) {

                    p_out << p_layout.m_id << ", "
                          << p_layout.m_orientation << ", "
                          << p_layout.m_text  << ", "
                          << p_layout.m_hint  << ", "
                          << p_layout.m_category  << ", "
                          << p_layout.m_coordinates;
                    return p_out;
                }
                
                // ==> layout - Public Constructors

                /** */
                layout() = delete;

                /** */
                layout(layout_id, const word &);

                /** */
                inline layout(const layout &);

                /** */
                inline layout(layout &&);

                /** Destructor */
                inline ~layout();

                // ==> layout - Public Accessors

                /** */
                inline const orientation & get_orientation() const noexcept;

                /** */
                inline const text & get_text() const ;

                /** */
                inline const category & get_category() const ;
                
                /** */
                inline const text & get_hint() const noexcept;
                
                /** */
                inline void set_orientation(const orientation & p_orientation)
                    noexcept; 

                /** */
                inline bool is_positioned() const noexcept;

                /** */
                inline const coordinates & get_coordinates() const noexcept;

                /** */
                inline layout_id get_id() const noexcept;

                /** */
                index get_size() const noexcept;
                
                // ==> layout - Public Operators

                /** */
                inline layout & operator=(const layout &) noexcept;

                /** */
                inline layout & operator=(layout &&) noexcept;

                /**  */
                inline bool operator ==(const layout &) const noexcept;

                /**  */
                inline bool operator !=(const layout &) const noexcept;
                
                /**  */
                inline bool operator !=(const text &) const noexcept;

                /**  */
                inline bool operator ==(const text &) const noexcept;

                /** not allowed */
                void* operator new (std::size_t size)
                    throw (std::bad_alloc) = delete; 

                /** not allowed */
                void operator delete (void *p_class_id) = delete;

                /** not allowed */
                void *operator new[] (size_t) = delete;

                /** not allowed */
                void operator delete[] (void *) = delete;


                // ==> layout - Public Processors

                // ==> layout - Public Attributes

                /** */
                static const class_id id = 14295422949564ULL;                

            private:

                // ==> layout - Private Internal Types

                

                // ==> layout - Private Accessors

                /** */
                coordinates & get_coordinates();
                
                
                // ==> layout - Private Processors

                /** */
                inline void unposition() noexcept;

                /** */
                void set_position(position p_line,
                                  position p_column,
                                  orientation p_orientation);

                /** */
                bool occupies(const coordinate & p_coord) const;

                // ==> layout - Private Attributes

                /** */
                layout_id m_id;

                /** */
                orientation m_orientation;

                /** */
                text m_hint;

                /** */
                text m_text;
                
                /** */
                category m_category;

                /** */
                coordinates m_coordinates;
            };
        }
    }
}
// =============================================================================
// I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::crosswords::concept;

// layout ######################################################################

template<typename supplier>
const class_id 
layout<supplier>::
id;


// -----------------------------------------------------------------------------
template<typename supplier>
layout<supplier>::
layout(layout_id p_layout_id, const word & p_word)
    : m_id(p_layout_id),
      m_orientation(orientation::undef),
      m_hint(p_word.get_hint()),
      m_text(p_word.get_value()),
      m_category(p_word.get_category()),
      m_coordinates(m_text) {}

// -----------------------------------------------------------------------------
template<typename supplier>
layout<supplier>::
layout(const layout & p_layout)
    : m_id(p_layout.m_id),
      m_orientation(p_layout.m_orientation),
      m_hint(p_layout.m_hint),
      m_text(p_layout.m_text),
      m_category(p_layout.m_category),
      m_coordinates(p_layout.m_coordinates) {}

// -----------------------------------------------------------------------------
template<typename supplier>
layout<supplier>::
layout(layout && p_layout) {
    m_id = std::move(p_layout.m_id);
    m_orientation = std::move(p_layout.m_orientation);
    m_hint = std::move(p_layout.m_hint);
    m_text = std::move(p_layout.m_text);
    m_category = std::move(p_layout.m_category);
    m_coordinates = std::move(p_layout.m_coordinates);
}

// -----------------------------------------------------------------------------
template<typename supplier>
layout<supplier>::
~layout() {}

// -----------------------------------------------------------------------------
template<typename supplier>
const typename layout<supplier>::orientation &
layout<supplier>::
get_orientation() const noexcept {return m_orientation;}

// -----------------------------------------------------------------------------
template<typename supplier>
const typename layout<supplier>::text &
layout<supplier>::
get_text() const {
    return m_text;
}

// -----------------------------------------------------------------------------
template<typename supplier>
typename layout<supplier>::layout_id
layout<supplier>::
get_id() const noexcept {return m_id;}

// -----------------------------------------------------------------------------
template<typename supplier>
const typename layout<supplier>::category &
layout<supplier>::
get_category() const {return m_category;}

// -----------------------------------------------------------------------------
template<typename supplier>
bool
layout<supplier>::
is_positioned() const noexcept {return m_coordinates.is_positioned();}

// -----------------------------------------------------------------------------
template<typename supplier>
void
layout<supplier>::
unposition() noexcept {
    m_coordinates.unposition();
}

// -----------------------------------------------------------------------------
template<typename supplier>
const typename layout<supplier>::text &
layout<supplier>::
get_hint() const noexcept {return m_hint;}

// -----------------------------------------------------------------------------
template<typename supplier>
void
layout<supplier>::
set_orientation(const orientation & p_orientation)  noexcept {
    m_orientation = p_orientation;
}

// -----------------------------------------------------------------------------
template<typename supplier>
layout<supplier> &
layout<supplier>::
operator=(const layout & p_layout) noexcept {
    if (this != & p_layout) {
        m_id = p_layout.m_id;
        m_orientation = p_layout.m_orientation;
        m_hint = p_layout.m_hint;
        m_text = p_layout.m_text;        
        m_category = p_layout.m_category;
        m_coordinates = p_layout.m_coordinates;
    }
    return *this;
}

// -----------------------------------------------------------------------------
template<typename supplier>
layout<supplier> &
layout<supplier>::
operator =(layout &&p_layout)  noexcept {
    if (this != & p_layout) {
        m_id = std::move(p_layout.m_id);
        m_orientation = std::move(p_layout.m_orientation);
        m_hint = std::move( p_layout.m_hint);
        m_text = std::move(p_layout.m_text); 
        m_category = std::move(p_layout.m_category);
        m_coordinates = std::move(p_layout.m_coordinates);
    }
    return *this;
}

// -----------------------------------------------------------------------------
template<typename supplier>
bool
layout<supplier>::
operator ==(const layout & p_layout) const noexcept {
    return (m_text == p_layout.m_text);
}

// -----------------------------------------------------------------------------
template<typename supplier>
bool
layout<supplier>::
operator !=(const layout & p_layout) const noexcept {
    return (m_text  != p_layout.m_text);
}

// -----------------------------------------------------------------------------
template<typename supplier>
bool
layout<supplier>::
operator ==(const text & p_text) const noexcept {
    return (m_text == p_text);
}

// -----------------------------------------------------------------------------
template<typename supplier>
bool
layout<supplier>::
operator !=(const text & p_text) const noexcept {
    return (m_text  != p_text);
}


// -----------------------------------------------------------------------------
template<typename supplier>
const typename layout<supplier>::coordinates &
layout<supplier>::
get_coordinates() const noexcept {return m_coordinates;}

// -----------------------------------------------------------------------------
template<typename supplier>
void
layout<supplier>::
set_position(position p_line, position p_column, orientation p_orientation) {
    
    index _last = m_coordinates.get_size();
    index _i = 0;

    if (p_orientation == orientation::hor) {
        for (; _i < _last; ++_i) {
            m_coordinates[_i].set_line(p_line);
            m_coordinates[_i].set_column(p_column++);
        }
    }
    else {
        for (; _i < _last; ++_i) {
            m_coordinates[_i].set_line(p_line++);
            m_coordinates[_i].set_column(p_column);
        }
    }
    m_orientation = p_orientation;
}

// -----------------------------------------------------------------------------
template<typename supplier>
typename layout<supplier>::coordinates & 
layout<supplier>::
get_coordinates() {return m_coordinates;}


// -----------------------------------------------------------------------------
template<typename supplier>
bool
layout<supplier>::
occupies(const coordinate & p_coord) const {
    index _size = m_coordinates.get_size();
    
    for (index _i = 0; _i < _size; ++_i) {
        if ( (m_coordinates[_i].get_line() == p_coord.get_line()) &&
             (m_coordinates[_i].get_column() == p_coord.get_column()) ) {
            return true;
        }
    }
    return false;
}

        




// -----------------------------------------------------------------------------
template<typename supplier>
typename layout<supplier>::index
layout<supplier>::
get_size() const noexcept {return m_coordinates.get_size();}


// // -----------------------------------------------------------------------------
// template<typename supplier>
// rc_t
// layout<supplier>::
// is_valid_position(layout & p_target) {
//     // position _line = m_coordinates[0].get_line();
//     // position _col = m_coordinates[0].get_column();

//     position _line ;
//     position _col;
//     index _last = m_coordinates.get_size();
    
//     if (m_orientation == orientation::hor) {
//         for (index _i = 0; _i < _last; ++_i) {
//             _line = m_coordinates[_i].get_line();
//             _col = m_coordinates[_i].get_column();
            
//             if (
//                 (p_target.collide(_line - 1, _col)) ||
//                 (p_target.collide(_line + 1, _col))
//                 ) {
//                 return rc_t(id, constants::coordinate_frontier_colision);
//             }

//             if (!p_target.collide(_line, _col, m_coordinates[_i].get_letter())) {
//                 return rc_t(id, constants::coordinate_value_unmatch);
//             }
//         }
//     }
//     else {
//         for (index _i = 0; _i < _last; ++_i) {
//             _line = m_coordinates[_i].get_line();
//             _col = m_coordinates[_i].get_column();
            
//             if (
//                 (p_target.collide(_line, _col - 1)) ||
//                 (p_target.collide(_line, _col + 1))
//                 ) {
//                 return rc_t(id, constants::coordinate_frontier_colision);
//             }

//             if (!p_target.collide(_line, _col, m_coordinates[_i].get_letter())) {
//                 return rc_t(id, constants::coordinate_value_unmatch);
//             }
//         }
//     }
    
//     return rc_t();
// }

    
    
#endif
